package com.taobao.utile;

/**
 * Twitter_Snowflake<br>
 * SnowFlake的结构如下(每部分用“-”符号分隔):<br>
 * 64位ID组成：<br>
 * 0 - 41位时间戳 - 5位数据中心标识 - 5位机器标识 - 12位序列号<br>
 * <p>
 * 每部分说明：<br>
 * 1位：不用。二进制中最高位为1的数表示负数，使用0保证生成的ID为正数<br>
 * 41位：毫秒级时间戳（可表示大约69年的时间）<br>
 * 5位：数据中心标识（dataCenterId）<br>
 * 5位：工作节点标识（machineId）<br>
 * 12位：毫秒内的序列（最大4096）<br>
 */
public class SnowflakeIdGenerator {

    // ==============================Fields===========================================
    /** 开始时间戳 (2023-01-01) */
    private final long twepoch = 1672531200000L;

    /** 机器id所占的位数 */
    private final long machineBits = 5L;

    /** 数据标识id所占的位数 */
    private final long dataCenterBits = 5L;

    /** 支持的最大机器id，结果是31 (这个移位算法可以很快计算出最大值：2^5 -1 ) */
    private final long maxMachineId = ~(-1L << machineBits);

    /** 支持的最大数据标识id，结果是31 */
    private final long maxDataCenterId = ~(-1L << dataCenterBits);

    /** 序列在id中占的位数 */
    private final long sequenceBits = 12L;

    /** 机器ID向左移位数 */
    private final long machineShift = sequenceBits;

    /** 数据中心ID向左移位数 */
    private final long dataCenterShift = sequenceBits + machineBits;

    /** 时间戳向左移位数 */
    private final long timestampShift = sequenceBits + machineBits + dataCenterBits;

    /** 生成序列的掩码（12位所对应的最大值） */
    private final long sequenceMask = ~(-1L << sequenceBits);

    /** 工作节点ID(0~31) */
    private long machineId;

    /** 数据中心ID(0~31) */
    private long dataCenterId;

    /** 毫秒内序列(0~4095) */
    private long sequence = 0L;

    /** 上次生成ID的时间戳 */
    private long lastTimestamp = -1L;

    //==============================Constructors=====================================
    /**
     * 构造函数
     * @param machineId 工作节点ID (0~31)
     * @param dataCenterId 数据中心ID (0~31)
     */
    public SnowflakeIdGenerator(long machineId, long dataCenterId) {
        if (machineId > maxMachineId || machineId < 0) {
            throw new IllegalArgumentException("machineId不能超过 " + maxMachineId);
        }
        if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
            throw new IllegalArgumentException("dataCenterId不能超过 " + maxDataCenterId);
        }
        this.machineId = machineId;
        this.dataCenterId = dataCenterId;
    }

    // ==============================Methods==========================================

    /**
     * 获得下一个ID (该方法是线程安全的)
     * @return Snowflake生成的ID
     */
    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();

        if (timestamp < lastTimestamp) {
            System.err.printf("时钟回拨: %d 毫秒\n", lastTimestamp - timestamp);
            throw new RuntimeException("时钟回拨");
        }

        if (timestamp == lastTimestamp) {
            // 相同毫秒内，序列号自增
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                // 当前毫秒内计数满了，获取下一时间戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            // 不同毫秒，序列号置0
            sequence = 0;
        }

        // 更新最后时间戳
        lastTimestamp = timestamp;

        // 移位并通过或运算拼到一起组成64位的ID
        return (timestamp - twepoch) << timestampShift //
                | dataCenterId << dataCenterShift //
                | machineId << machineShift //
                | sequence;
    }

    /**
     * 阻塞到下一个毫秒，直到获得新的时间戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    // ==============================Test=============================================
    public static void main(String[] args) {
        // 示例：创建一个Snowflake实例，machineId=1, dataCenterId=1
        SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);

        for (int i = 0; i < 10; i++) {
            long id = idGenerator.nextId();
            System.out.println("生成ID：" + id);
        }
    }
}